package boardgamer.common.board;

import boardgamer.common.annotations.Immutable;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;

import java.util.Collection;

/**
 * You can specify the width of 3 sides of the hexagon: x, y, and z. The origin
 * is one of the corners of the hexagon. The two adjacent edges and their
 * opposing edges have length x and y. The remaining edges have length z.
 *
 * @author garysoed
 */
@Immutable
public class HexagonBoard<T> extends TiledBoard<T> {

  protected final int x;
  protected final int y;
  protected final int z;
  protected final Table<Integer, Integer, T> board;

  // for serialization
  protected HexagonBoard() {
    super();
    this.x = 1;
    this.y = 1;
    this.z = 1;
    board = HashBasedTable.create();
  }

  public HexagonBoard(TileFactory<T> tileFactory, int x, int y, int z) {
    super(tileFactory);
    this.x = x;
    this.y = y;
    this.z = z;

    int xWidth = x - 1;
    int yWidth = y - 1;
    int zWidth = z - 1;

    board = HashBasedTable.create(xWidth + zWidth, yWidth + zWidth);
    for (int xi = 0; xi <= xWidth + zWidth; xi++) {
      int minY = Math.max(0, xi - xWidth);
      int maxY = Math.min(yWidth + zWidth, yWidth + xi);

      for (int yi = 0; yi <= yWidth + zWidth; yi++) {
        if (yi >= minY && yi <= maxY) {
          board.put(xi, yi, tileFactory.createTile(xi, yi));
        }
      }
    }
  }

  public HexagonBoard(TileFactory<T> tileFactory, int side) {
    this(tileFactory, side, side, side);
  }

  @Override
  public Collection<T> getTiles() {
    return getBoard().values();
  }

  @Override
  public Table<Integer, Integer, T> getBoard() {
    return HashBasedTable.create(board);
  }

  @Override
  public boolean hasTile(int x, int y) {
    return board.contains(x, y);
  }

  @Override
  protected T findTile(int x, int y) {
    return board.get(x, y);
  }


  public int getX() {
    return x;
  }

  public int getY() {
    return y;
  }

  public int getZ() {
    return z;
  }
}
